#include "string_util.h"

#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QUuid>
#include <fstream>

#include "../FileSys.h"

#ifndef WIN32
#define _countof(x) (sizeof(x) / sizeof((x)[0]))
#endif

namespace qb {
namespace base {
namespace stringutil {
enum {
  MAX_CHAR_SIZE = 256,
};
}

#define TOLOWERCHAR(C) ((C >= 'A' && C <= 'Z') ? (C + ('a' - 'A')) : C)

int strrfind(const char* str, int ipos, char c) {
  for (int i = ipos; i >= 0; --i)
    if (str[i] == c) return i;

  return -1;
}
static char inline GetCaseChar(bool igoreCase, const char& c) {
  return (!igoreCase && (c >= 'A' && c <= 'Z')) ? c + 32 : c;
}
static int findpos(int* intarr, int count, int v) {
  for (int i = 0; i < count; i++) {
    if (intarr[i] == v) return i;
  }
  return -1;
}

int StringUtil::SplitString(const char* str, int size, const BaseString& key,
                            std::vector<BaseString>& strings) {
  std::vector<strinfo> lineinfos;
  SplitStringInfo(str, size, key, lineinfos);
  for (auto it = lineinfos.begin(); it != lineinfos.end(); ++it) {
    if (it->len <= 0) continue;
    strings.push_back(BaseString(str + it->pos, it->len));
  }
  return strings.size();
}

int StringUtil::SplitStringInfo(const char* str, int size,
                                const BaseString& key, strinfo* strings,
                                int capcity) {
  int start = 0;
  int ipos = 0;
  for (int i = 0; i < size; ++i) {
    if (key.find(str[i]) != BaseString::npos) {
      if (i > start && ipos < capcity) {
        strinfo info;
        info.pos = start;
        info.len = i - start;
        strings[ipos++] = info;
      }
      start = i + 1;
    }
  }
  if (size > start && ipos < capcity) {
    strinfo info;
    info.pos = start;
    info.len = size - start;
    strings[ipos++] = info;
  }
  return ipos;
}

int StringUtil::SplitStringInfo(const char* str, int size,
                                const BaseString& key,
                                std::vector<strinfo>& strings) {
  int start = 0;
  for (int i = 0; i < size; ++i) {
    if (key.find(str[i]) != BaseString::npos) {
      if (i >= start) {
        strinfo info;
        info.pos = start;
        info.len = i - start;
        strings.push_back(info);
      }
      start = i + 1;
    }
  }
  if (size > start) {
    strinfo info;
    info.pos = start;
    info.len = size - start;
    strings.push_back(info);
  }
  return strings.size();
}

int StringUtil::SplitStringInfo(const char* str, int size,
                                const BaseString& key,
                                SimpleArray<strinfo>& strings) {
  int start = 0;
  for (int i = 0; i < size; ++i) {
    if (key.find(str[i]) != BaseString::npos) {
      if (i >= start) {
        strinfo info;
        info.pos = start;
        info.len = i - start;
        strings.push_back(info);
      }
      start = i + 1;
    }
  }
  if (size > start) {
    strinfo info;
    info.pos = start;
    info.len = size - start;
    strings.push_back(info);
  }
  return strings.size();
}

int StringUtil::SplitStringInfo(const wchar_t* str, int size,
                                const WideString& key, strinfo* strings,
                                int capcity) {
  int start = 0;
  int ipos = 0;
  for (int i = 0; i < size; ++i) {
    if (key.find(str[i]) != WideString::npos) {
      if (i > start && ipos < capcity) {
        strinfo info;
        info.pos = start;
        info.len = i - start;
        strings[ipos++] = info;
      }
      start = i + 1;
    }
  }
  if (size > start && ipos < capcity) {
    strinfo info;
    info.pos = start;
    info.len = size - start;
    strings[ipos++] = info;
  }
  return ipos;
}

int StringUtil::SplitStringInfoEx(const char* str, int size,
                                  const BaseString& key,
                                  std::vector<strinfo>& tokens,
                                  bool ignoreZeroLength /*= true*/) {
  int start = 0;
  for (int i = 0; i < size; ++i) {
    if (key.find(str[i]) != BaseString::npos) {
      if ((ignoreZeroLength && i > start) ||
          (!ignoreZeroLength && i >= start)) {
        strinfo info;
        info.pos = start;
        info.len = i - start;
        tokens.push_back(info);
      }
      start = i + 1;
    }
  }
  if ((ignoreZeroLength && size > start) ||
      (!ignoreZeroLength && size >= start)) {
    strinfo info;
    info.pos = start;
    info.len = size - start;
    tokens.push_back(info);
  }
  return tokens.size();
}

void StringUtil::SplitFileNameEx(const BaseString& full, BaseString& path,
                                 BaseString& base, BaseString& ext) {
  int ipos = full.rfind('/');
  ipos = ipos < 0 ? full.rfind('\\') : ipos;
  if (ipos >= 0) {
    path = full.substr(0, ipos);
    BaseString name = full.substr(ipos + 1, full.size() - ipos - 1);
    int dotpos = name.rfind('.');
    if (dotpos >= 0) {
      base = name.substr(0, dotpos);
      ext = name.substr(dotpos + 1, name.size() - 1 - dotpos);
    } else {
      base = name;
      ext = "";
    }
  }
}

void StringUtil::SplitFileName(const BaseString& full, BaseString& path,
                               BaseString& name) {
  int iend = -1;
  int i = full.size() - 1;
  for (; i >= 0; i--) {
    bool isslash = full[i] == '/' || full[i] == '\\';
    if (iend < 0) {
      if (isslash) iend = i;
    } else {
      if (!isslash) {
        i++;
        break;
      }
    }
  }
  if (i >= 0 && i <= iend && iend >= 0) {
    path = full.substr(0, i);
    name = full.substr(iend + 1, full.size() - iend - 1);
  }
}

int StringUtil::FindInSet(const char* str, int len, BaseString strset) {
  for (int i = len - 1; i >= 0; i--) {
    if (strset.find(str[i]) != BaseString::npos) return i;
  }
  return -1;
}

char* StringUtil::LoadFileContent(const wchar_t* fileName, int& fileSize) {
  fileSize = 0;
  QString filePath = QString::fromWCharArray(fileName);
  QFile file(filePath);
  if (file.open(QIODevice::ReadOnly)) return nullptr;

  fileSize = file.size();
  QDataStream ds(&file);

  char* data = new char[fileSize];
  memset(data, 0, fileSize);
  ds.readRawData(data, fileSize);
  file.close();
  return data;
}

int StringUtil::LoadLinesFromFile(const wchar_t* fileName,
                                  std::vector<BaseString>& lines,
                                  const BaseString& key /*= "\r\n"*/,
                                  bool skipBlankLines /*= true*/) {
  int fsize = 0;
  char* buf = LoadFileContent(fileName, fsize);
  if (buf == NULL || fsize <= 0) return 0;
  if (skipBlankLines)
    SplitString(buf, fsize, key, lines);
  else {
    std::vector<strinfo> lineinfos;
    SplitStringInfoEx(buf, fsize, key, lineinfos, skipBlankLines);
    int zero_index = 0;
    for (auto it = lineinfos.begin(); it != lineinfos.end(); ++it) {
      strinfo& si = *it;
      zero_index = si.len <= 0 ? (zero_index + 1) : 0;
      if (zero_index % 2 == 1) continue;
      lines.push_back(BaseString(buf + si.pos, si.len));
    }
  }
  delete[] buf;
  return lines.size();
}

int strrfind2(const char* str, int ipos, char c) {
  for (int i = ipos; i >= 0; --i)
    if (str[i] == c) return i;

  return -1;
}

const char* StringUtil::GetShortFileName(const char* fileName, int count) {
  if (fileName == NULL) return NULL;
  int len = strlen(fileName);
  int ipos = len - 1;

  int num = 0;
  while (num < count) {
    int newpos = strrfind2(fileName, ipos, '\\');
    if (newpos < 0) break;
    ipos = newpos - 1;
    num++;
  }
  if (num <= 0) return fileName;

  const char* short_name = fileName + ipos + 1;
  return short_name;
}

template <class Container>
bool StringUtil::WriteLinesImpl(const Container& lines,
                                const BaseString& fileName, bool append) {
  QFile* file;
  FileSys::instance()->createFile(fileName.c_str(), QIODevice::ReadWrite, file);
  if (file == nullptr) return false;

  int fsize = 0;
  for (typename Container::const_iterator it = lines.begin(); it != lines.end();
       ++it) {
    fsize += it->size() + 2;
  }
  if (append) fsize += 2;
  char* buf = new char[fsize + 1];
  char* ptr = buf;
  memset(buf, 0, fsize + 1);
  if (append) {
    //*ptr++	= '\r';
    *ptr++ = '\n';
  }
  for (typename Container::const_iterator it = lines.begin(); it != lines.end();
       ++it) {
    memcpy(ptr, it->c_str(), it->size());
    ptr += it->size();
    //*ptr++	= '\r';
    *ptr++ = '\n';
  }

  if (append) {
    file->seek(file->size());
  } else {
    file->seek(0);
  }

  file->write(buf, fsize);
  FileSys::instance()->closeFile(file);

  return true;
}
bool StringUtil::WriteLines(const SimpleArray<BaseString>& lines,
                            const BaseString& fileName, bool append) {
  return WriteLinesImpl<SimpleArray<BaseString>>(lines, fileName, append);
}
bool StringUtil::WriteLines(const std::vector<BaseString>& lines,
                            const BaseString& fileName, bool append) {
  return WriteLinesImpl<std::vector<BaseString>>(lines, fileName, append);
}
bool StringUtil::WriteLines(const std::set<BaseString>& lines,
                            const BaseString& fileName, bool append) {
  return WriteLinesImpl<std::set<BaseString>>(lines, fileName, append);
}

bool StringUtil::IsNumber(const BaseString& str) {
  return IsNumber(str.c_str(), str.size());
}

bool StringUtil::IsNumber(const char* str, int len) {
  char c = 0;
  for (int i = 0; i < len; i++) {
    c = str[i];
    if ((c < '0' || c > '9') && c != '.') return false;
  }
  return true;
}

void StringUtil::tohex(unsigned char c, char str[]) {
  static char codes[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  str[0] = codes[c >> 4];
  str[1] = codes[c & 0x0f];
}

int StringUtil::tohex(wchar_t c, char* str) {
  static char codes[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  unsigned char low = c & 0x00ff;
  unsigned char hi = (c & 0xff00) >> 8;
  int i = 0;
  str[i++] = '0';
  str[i++] = 'x';
  if (hi != 0) {
    str[i++] = codes[hi >> 4];
    str[i++] = codes[hi & 0x0f];
  }
  str[i++] = codes[low >> 4];
  str[i++] = codes[low & 0x0f];
  return i;
}

QString StringUtil::GenerateGuidString() {
  QUuid id = QUuid::createUuid();
  return id.toString();
}
bool StringUtil::SaveFileContent(const char* buf, int size,
                                 const wchar_t* fileName) {
  QString strPath = QString::fromWCharArray(fileName);
  QFileInfo fileInfo(strPath);
  if (!fileInfo.absoluteDir().mkpath(fileInfo.absolutePath())) return false;

  QFile file(strPath);
  if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false;
  file.write(buf, size);
  file.close();
  return true;
}

int StringUtil::GetStringRealBytes(const std::string& str) {
  int bytes = str.size() + 1;
  return bytes;
}

static int CalcCapcityBySize(int size) {
  // 最大处理1mb的尺寸
  static int capcitys[] = {15,     31,     47,     70,     105,    157,
                           235,    352,    528,    792,    1188,   1782,
                           2673,   4009,   6013,   9019,   13528,  20292,
                           30438,  45657,  68485,  102727, 154090, 231135,
                           346702, 520053, 780079, 1170118};
  for (int i = 0; i < _countof(capcitys); i++) {
    if (size <= capcitys[i]) return capcitys[i];
  }
  return size;
}
int StringUtil::GetStringAllBytes(const std::string& str) {
  int bytes = sizeof(std::string);
  const char* base = (const char*)&str;
  if (str.c_str() < base || str.c_str() >= base + bytes) {
    // 考虑到对齐,实际占用还要多
    /*	capcity     15 == > 31  size:    16
            capcity     31 == > 47  size:    32
            capcity     47 == > 70  size:    48
            capcity     70 == > 105  size:    71
            capcity    105 == > 157  size:   106
            capcity    157 == > 235  size:   158
            capcity    235 == > 352  size:   236
            capcity    352 == > 528  size:   353
            capcity    528 == > 792  size:   529
            capcity    792 == > 1188  size:   793
            0-15	==>	16
            16-31	==>	31
            32-47	==>	47
            48-70	==> 70
            71-105	==>	105
    */

    bytes += CalcCapcityBySize(str.size()) + 1;
  }
  return bytes;
}

void StringUtil::StringCopy(char* dest, int capcity, const char* source) {
  int i = 0;
  if (dest && source && capcity > 0) {
    while (*source && i + 1 < capcity) {
      dest[i++] = *source++;
    }
    dest[i] = 0;
    dest[capcity - 1] = 0;
  }
}

char* StringUtil::_strstr_cn(const char* str, char* data) {
  int index;
  int len;

  if (nullptr == str || nullptr == str) return nullptr;

  len = strlen(data);
  if ((int)strlen(str) < len) return nullptr;

  char cSrc;
  while (*str) {
    for (index = 0; index < len; index++) {
      cSrc = str[index];
      if (cSrc >= 'a' && cSrc <= 'z') {
        cSrc -= 'a';
        cSrc += 'A';
      }
      if (cSrc != data[index]) break;
    }

    if (index == len) return (char*)str;

    if ('\0' == str[len]) break;

    if (str[0] < 0) {
      if ('\0' == str[len + 1])
        break;
      else
        str += 2;
    } else
      str++;
  }

  return nullptr;
}

}  // namespace base
}  // namespace qb
